import asyncio
import logging
import random
import math
from pymodbus.server import StartAsyncSerialServer
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.device import ModbusDeviceIdentification

# 配置串口参数
SERIAL_PORT = "COM1"  # 根据你的系统修改
BAUDRATE = 9600
UNIT_ID = 1

# 设置日志
logging.basicConfig(
    format="%(asctime)s [%(levelname)s] %(message)s",
    level=logging.DEBUG
)

class DynamicDataBlock(ModbusSequentialDataBlock):
    """动态数据块，支持多种寄存器类型的动态变化"""
    def __init__(self, address, size, reg_type, initial_offset=0):
        self.reg_type = reg_type  # 'coil', 'discrete', 'holding', 'input'
        if reg_type in ['coil', 'discrete']:
            # 线圈和离散输入使用布尔值
            self.values = [bool((initial_offset + i) % 2) for i in range(size)]
        else:
            # 寄存器和输入寄存器使用整数值
            self.values = [(initial_offset + i) % 65536 for i in range(size)]
        
        super().__init__(address, self.values.copy())
        self.counter = 0
        self.size = size
        self.address = address
        
    def update_values(self):
        """根据寄存器类型更新所有值"""
        self.counter += 1
        
        if self.reg_type == 'coil':
            # 线圈值随机翻转
            for i in range(self.size):
                if random.random() > 0.8:  # 20%概率翻转
                    self.values[i] = not self.values[i]
        
        elif self.reg_type == 'discrete':
            # 离散输入模拟开关量输入
            for i in range(self.size):
                # 模拟周期性变化
                self.values[i] = (i + self.counter) % 10 < 5
        
        elif self.reg_type == 'holding':
            # 保持寄存器模拟可写寄存器
            for i in range(self.size):
                # 基础值 + 计数器 + 正弦波动
                base = 100 + i * 10
                wave = 50 * math.sin(self.counter * 0.1 + i * 0.05)
                self.values[i] = int(base + wave) % 65536
        
        elif self.reg_type == 'input':
            # 输入寄存器模拟只读传感器数据
            for i in range(self.size):
                # 模拟温度传感器(20-30度) + 随机噪声
                base = 2000 + i * 100  # 20.00度起，每个地址增加1度
                noise = random.randint(-50, 50)  # ±0.5度
                self.values[i] = (base + noise) % 65536
        
        # 更新底层数据块
        if self.reg_type in ['coil', 'discrete']:
            self.setValues(self.address, [int(v) for v in self.values])
        else:
            self.setValues(self.address, self.values)

class DynamicSlaveContext(ModbusSlaveContext):
    """支持所有寄存器类型动态更新的从站上下文"""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.update_task = None
        
    async def start_updates(self, interval=1.0):
        """启动后台更新任务"""
        async def update_loop():
            while True:
                try:
                    # 更新所有数据块
                    for reg_type, block in self.store.items():
                        if hasattr(block, 'update_values'):
                            block.update_values()
                    
                    await asyncio.sleep(interval)
                except Exception as e:
                    logging.error(f"Update error: {e}")
                    await asyncio.sleep(interval)
                    
        self.update_task = asyncio.create_task(update_loop())

# 创建数据存储
def create_datastore():
    store = DynamicSlaveContext(
        di=DynamicDataBlock(1, 10000, 'discrete'),  # 离散输入
        co=DynamicDataBlock(1, 10000, 'coil'),      # 线圈
        hr=DynamicDataBlock(1, 10000, 'holding'),   # 保持寄存器
        ir=DynamicDataBlock(1, 10000, 'input')      # 输入寄存器
    )
    return store

# 设备标识信息
identity = ModbusDeviceIdentification()
identity.VendorName = "FullDynamicServer"
identity.ProductCode = "FDS-1"
identity.ModelName = "Fully Dynamic Modbus Server"

async def run_server():
    # 创建数据存储和上下文
    store = create_datastore()
    context = ModbusServerContext(slaves=store, single=True)
    
    # 启动后台更新
    await store.start_updates(0.5)  # 每0.5秒更新一次
    
    # 启动服务器
    server = await StartAsyncSerialServer(
        context=context,
        identity=identity,
        port=SERIAL_PORT,
        baudrate=BAUDRATE,
        timeout=1,
        unit=UNIT_ID
    )
    
    logging.info(f"Server started on {SERIAL_PORT}")
    logging.info("All register types are now dynamically updating:")
    logging.info("- Coils: Randomly flipping (20% chance per update)")
    logging.info("- Discrete Inputs: Simulating periodic on/off patterns")
    logging.info("- Holding Registers: Sine wave patterns")
    logging.info("- Input Registers: Simulating temperature sensors with noise")
    
    try:
        await server.serve_forever()
    except asyncio.CancelledError:
        server.close()
        await server.wait_closed()

if __name__ == "__main__":
    try:
        asyncio.run(run_server())
    except KeyboardInterrupt:
        logging.info("Server shutdown")